#!/usr/bin/env python

import sys
import IPy

from roster_user_tools import cli_record_lib
from roster_user_tools import cli_common_lib
from roster_user_tools import roster_client_lib
from roster_user_tools.data_flags import MassAdd
from roster_user_tools.action_flags import Make


class Args(Make, MassAdd):
  pass


def main(args):
  """Collects command line arguments, writes hosts from file, and
  enumerates any overwritten hosts.
  Inputs:
    args: list of arguments from the commandline

  Outputs: 
    List of IP's and hostnames that were overwritten.
  """
  command = 'add'
  if( args and not args[0].startswith('-') ):
    command = args.pop(0)
  usage = ('\n'
          '\n'
          'To add a list of hosts from a file:\n'
          '\t%s -f <file-name> -v <view-name> -z <zone-name>\n'
          '\nFile example:\n'
          '192.168.1.5 computer1\n'
          '192.168.1.8 host3\n'
          '\n' % tuple(
              [sys.argv[0] for _ in range(1)]))
  args_instance = Args(command, ['add'], args, usage)
  options = args_instance.options
  
  try:
    cli_common_lib_instance = cli_common_lib.CliCommonLib(options)
  except cli_common_lib.ArgumentError, error:
    print 'ERROR: %s' % error
    sys.exit(1)
  cli_record_lib.CliRecordLib(cli_common_lib_instance)
  try:
    handle = open(options.file)
  except IOError:
    print ('Specified file, %s, does not exist' % (options.file))
    sys.exit(1)
  try:
    file_lines = handle.readlines()
  finally:
    handle.close()
  records_to_remove = []
  records_to_add = []
  for line in file_lines:
    if not line.strip():
      continue
    ip_address = line.rsplit()[0].strip()
    host_name = line.rsplit()[1].strip()
    ## FIND TYPE
    record_type = IPy.IP(ip_address).version()
    if( record_type == 4 ):
      record_type = u'a'
    elif( record_type == 6 ):
      record_type = u'aaaa'
      ip_address = IPy.IP(ip_address).strFullsize()
    ## ADD TO ADDLIST
    record_dict = {
        'record_type': record_type,
        'record_ttl': 3600,
        'record_target': host_name,
        'record_view_dependency': options.view_name,
        'record_zone_name': options.zone_name,
        'record_arguments': {'assignment_ip': ip_address}}
    records_to_add.append(record_dict)

    zone_list = roster_client_lib.RunFunction(
          'ListZones', options.username, credfile=options.credfile,
          server_name=options.server,
          kwargs={'zone_name': options.zone_name})['core_return']
    if( options.zone_name not in zone_list ):
      cli_common_lib.DnsError(
          'Zone "%s" does not exist.' % options.zone_name, 1)
    if( options.view_name not in zone_list[options.zone_name] ):
      cli_common_lib.DnsError('Zone "%s" not found in "%s" view.' % (
          options.zone_name, options.view_name), 1)
    zone_origin = zone_list[options.zone_name][options.view_name][
        'zone_origin']
    if( host_name.rstrip('.').endswith(zone_origin.rstrip('.')) ):
      cli_common_lib.DnsError(
          'Hostname cannot end with domain name.', 1)
    if( zone_origin.endswith('.in-addr.arpa.') or
        zone_origin.endswith('.ip6.arpa.') ):
      cli_common_lib.DnsError(
          'This tool requires a forward zone as an argument. '
          'Reverse zones are handled automatically.', 1)
    reverse_zone_name = roster_client_lib.RunFunction(
        'ListZoneByIPAddress', options.username, credfile=options.credfile,
        server_name=options.server, args=[ip_address])['core_return']
    if( reverse_zone_name == None ):
      cli_common_lib.DnsError(
          'No reverse zone found for ip "%s"' % ip_address, 1)
    zone_cidr = roster_client_lib.RunFunction(
        'ListReverseRangeZoneAssignments',
        options.username, credfile=options.credfile,
        server_name=options.server, kwargs={'zone_name': reverse_zone_name})[
            'core_return'][reverse_zone_name]
    net_mask = int(IPy.IP(zone_cidr).netmask().strDec(0))
    masked_ip = ~net_mask & int(IPy.IP(ip_address).strDec(0))
    ptr_target = IPy.IP(masked_ip).reverseName()
    ptr_target = ptr_target.rstrip('.in-addr.arpa.').rstrip('.ip6.arpa.')
    ptr_target_split = ptr_target.split('.')
    cidr_size = int(zone_cidr.split('/')[1])
    if( record_type == 'a' ):
      ptr_target = '.'.join(ptr_target_split[:4 - (cidr_size / 8)])
    else:
      ptr_target = '.'.join(ptr_target_split[:128 - cidr_size + 1])

    ## ADD TO ADDLIST
    ptr_dict = {
        'record_type': u'ptr',
        'record_target': ptr_target,
        'record_ttl': 3600,
        'record_view_dependency': options.view_name,
        'record_zone_name': reverse_zone_name,
        'record_arguments': {
        'assignment_host': '%s.%s' % (host_name, zone_origin)}}
    records_to_add.append(ptr_dict)

    ## FIND CONFLICTING RECORDS
    records = roster_client_lib.RunFunction(
        "ListRecordsByCIDRBlock",
        options.username, credfile=options.credfile,
        server_name=options.server, args=[ip_address])['core_return']
    ## ADD CONFLICTS TO REMOVELIST
    for view in records:
      for record_ip in records[view]:
        for record in iter(records[view][record_ip]):
          records_to_remove.append(record)

  for record in records_to_remove:    
    print 'Host: %s with ip address %s will be REMOVED'% (
          record['host'], record['record_args_dict']['assignment_ip'])

  count = 0
  print_list = []
  for record in records_to_add:
    count += 1
    if ( record['record_zone_name'] == 'forward_zone' ):
      print_list.append(record['record_arguments']['assignment_ip'])
    else:
      print_list.append(record['record_arguments']['assignment_host'])
      print 'Host: %s with ip address %s will be ADDED'% (
          print_list [count - 1], print_list [count - 2])
    
  while( not options.commit and not options.no_commit ):
    yes_no = raw_input('Do you want to commit these changes? (y/N): ')
    if( yes_no.lower() not in ['y', 'yes', 'n', 'no', ''] ):
      continue
    if( yes_no.lower() in ['n', 'no', ''] ):
      options.no_commit = True
    if( yes_no.lower() in ['y', 'yes'] ):
      options.commit = True
    if( options.no_commit ):
      print 'No changes made.'
      sys.exit(0)

 
  if( options.commit ):
    roster_client_lib.RunFunction(
        "ProcessRecordsBatch", options.username, credfile=options.credfile,
        server_name=options.server,
        kwargs={'delete_records': records_to_remove,
            'add_records': records_to_add})
    print 'Records Committed.'

  if( options.no_commit ):
    print 'No changes made.'

if __name__ == "__main__":
  main(sys.argv[1:])
